package unsafe

import (
	"geektime-go-homework/web/orm/internal/errs"
	"reflect"
	"unsafe"
)

type FieldMeta struct {
	// 字段的偏移量
	offset uintptr
	// 通过反射获得的类型
	typ reflect.Type
}

// Accessor
//
//	@Description: 字段体
type Accessor struct {
	//
	//  fields
	//  @Description: 字段列表
	//
	fields map[string]FieldMeta
	//
	//  address
	//  @Description: 字段对应的起始地址
	//
	address unsafe.Pointer
}

// NewUnsafeAccessor
//
//	@Description: 初始化反射者
//	@param entity
//	@return *Accessor
func NewUnsafeAccessor(entity any) *Accessor {
	// 通过反射获取类型
	typ := reflect.TypeOf(entity)
	// 获取真正的类型
	typ = typ.Elem()
	// 获取字段数量
	numField := typ.NumField()
	fields := make(map[string]FieldMeta, numField)
	for i := 0; i < numField; i++ {
		// 通过反射获取字段
		fd := typ.Field(i)
		fields[fd.Name] = FieldMeta{
			// 字段类型
			typ: fd.Type,
			// 字段偏移量
			offset: fd.Offset,
		}
	}
	val := reflect.ValueOf(entity)
	return &Accessor{
		// 字段列表
		fields: fields,
		// 字段起始地址
		address: val.UnsafePointer(),
	}
}

// Field
//
//	@Description: 获取字段
//	@receiver a
//	@param field
//	@return any
//	@return error
func (a *Accessor) Field(field string) (any, error) {
	fd, ok := a.fields[field]
	if !ok {
		return nil, errs.NewErrUnknownField(field)
	}
	// 获取字段起始地址
	fdAddress := unsafe.Pointer(uintptr(a.address) + fd.offset)
	// 不知道确切类型,通过反射推断类型
	return reflect.NewAt(fd.typ, fdAddress).Elem().Interface(), nil
}

// SetField
//
//	@Description: 设置字段类型
//	@receiver a
//	@param field
//	@param val
//	@return error
func (a *Accessor) SetField(field string, val any) error {
	fd, ok := a.fields[field]
	if !ok {
		return errs.NewErrUnknownField(field)
	}
	// 获取字段起始地址
	fdAddress := unsafe.Pointer(uintptr(a.address) + fd.offset)
	// 不知道确切类型，仍然需要通过反射
	reflect.NewAt(fd.typ, fdAddress).Elem().Set(reflect.ValueOf(val))
	return nil
}
